热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

之和|笔者_图学习参考资料2知识补充与node2vec代码注解

篇首语:本文由编程笔记#小编为大家整理,主要介绍了图学习参考资料2-知识补充与node2vec代码注解相关的知识,希望对你有一定的参考价值。 本项目参考: https://aistudi

篇首语:本文由编程笔记#小编为大家整理,主要介绍了图学习参考资料2-知识补充与node2vec代码注解相关的知识,希望对你有一定的参考价值。


本项目参考:
https://aistudio.baidu.com/aistudio/projectdetail/5012408?contributionType=1


*一、正题篇:DeepWalk、word2vec、node2vec

其它相关项目:

关于图计算&图学习的基础知识概览:前置知识点学习(PGL)[系列一] https://aistudio.baidu.com/aistudio/projectdetail/4982973?contributionType=1

图机器学习(GML)&图神经网络(GNN)原理和代码实现(前置学习系列二):https://aistudio.baidu.com/aistudio/projectdetail/4990947?contributionType=1


1.1 DeepWalk算法流程

【图来源:网络,笔记由笔者添上】

算法流程:

【其中使用skip-gram模型是为了利用梯度的方法对参数进行更新训练】


1.2 Skip Gram算法流程

【图来源:网络,笔记由笔者添上】

算法流程:

【其中使用skip-gram模型是为了利用梯度的方法对参数进行更新训练】

此外,关于以上两个算法的定义和概念就不一一展示说明了,后边等有空了补上。下边说一下我学习的一些参考资料,希望对大家有帮助。


参考资料

1.【论文笔记】DeepWalk——陌上疏影凉

1.【网络图模型综述

1.【异构图神经网络简介–机器之心

1.【Graph Embedding之metapath2vec–圈圈_Master

1.【从Random Walk谈到Bacterial foraging optimization algorithm(BFOA),再谈到Ramdom Walk Graph Segmentation图分割算法



二 程序注解

【注解展示,是为了方便自己理解,同时也希望能帮到和自己一样在学习这块知识的小伙伴】


1.0 DeepWalk随机游走的实现



实现Graph类的random_walk函数–可参考


1.1 实现的参考代码–DeepWalk的随机游走算法实现

思路


  1. 理清successor,outdegree函数的输入输出
  2. 查看补全函数的返回类型–分析可能的结果–我最初的猜测:这walks应该是多个向量的集合,最后确实也是【[[],…]这样的结构多用于扩充,然后联想需要学习向量,所以想到向量递推的那种向量集合】
  3. 生成随机采样索引序列集 – 这个需要匹配采样形状&#xff0c;以及索引阈值–我是参考一个示例修改的&#xff0c;感悟是: 利用随机数0~1&#xff0c;给rand传入指定shape&#xff0c;再乘以一个相同shape的数据&#xff0c;可以得到一个>&#61;0, <&#61;最大出度-1的值&#xff0c;这样就可以用来索引采样了
  4. 理清上下文变量关系&#xff0c;进行zip打包遍历到一起&#xff0c;然后进行聚合操作即可。


具体注解在代码中&#xff0c;可查阅&#xff0c;基本都是按照一行行注解的

from pgl.graph import Graph
import numpy as np
class UserDefGraph(Graph):
def random_walk(self, nodes, walk_len):
"""
输入&#xff1a;nodes - 当前节点id list (batch_size,)
walk_len - 最大路径长度 int
输出&#xff1a;以当前节点为起点得到的路径 list (batch_size, walk_len)
用到的函数
1. self.successor(nodes)
描述&#xff1a;获取当前节点的下一个相邻节点id列表
输入&#xff1a;nodes - list (batch_size,)
输出&#xff1a;succ_nodes - list of list ((num_successors_i,) for i in range(batch_size))
2. self.outdegree(nodes)
描述&#xff1a;获取当前节点的出度
输入&#xff1a;nodes - list (batch_size,)
输出&#xff1a;out_degrees - list (batch_size,)
"""
walks &#61; [[node] for node in nodes] # 首先获得当前节点列表对应的一个向量
walks_ids &#61; np.arange(0, len(nodes)) # 游走路径中节点对应id号
cur_nodes &#61; np.array(nodes) # 当前节点情况
for l in range(walk_len): # 根据游走长度进行遍历--破出条件&#xff1a;1. range结束&#xff1b;2. outdegree&#61;&#61;0【出度为零&#xff0c;没有可继续的节点】
"""选取有下一个节点的路径继续采样&#xff0c;否则结束"""
outdegree &#61; self.outdegree(cur_nodes) # 计算当前节点的出度--也就是对应有哪些位置的邻近节点
walk_mask &#61; (outdegree !&#61; 0) # 根据出度来确定掩码--True&#xff0c; False--将出度为0的部分复制为False&#xff0c;反之True
if not np.any(walk_mask): # 判断是否没有可继续的节点情况--出度为0
break
cur_nodes &#61; cur_nodes[walk_mask] # 根据掩码获取可继续前进的节点&#xff0c;作为后边讨论的当前可前行节点
walks_ids &#61; walks_ids[walk_mask] # 获取掩码下&#xff0c;原节点id&#xff0c;组成新的work_ids用于后边讨论&#xff0c;但本身还是作为一个节点的标记&#xff0c;对应这是第几个节点
outdegree &#61; outdegree[walk_mask] # 根据掩码获取相应的不为0的出度--用于后边计算前行的路径
######################################
# 请在此补充代码采样出下一个节点
&#39;&#39;&#39;
[注解有点多&#xff0c;所以放外边了]
PS:
1. successor 可获取当前节点的下一个相邻节点id列表&#xff0c;
那么successor 计算出下一节点的集合后&#xff0c;我们需要从中随机取出一个节点--所以我们要创建随机采样的index_list&#xff08;索引序列集&#xff09;
2. 创建index_list&#61;>为了才到合适的index信息&#xff0c;采用np.floor与np.random,rand()实现&#xff1a;
eg: np.floor(np.random.rand(outdegree.shape[0]) * outdegree).astype(&#39;int64&#39;)
np.random.rand(outdegree.shape[0]): 根据出度集的形状来取得相应形状的随机数--这里体现游走的随机性
np.random.rand(outdegree.shape[0]) * outdegree&#xff1a;利用生成的随机数与出度集对应元素相乘——这里得到一些列的随机数&#xff0c;随机数范围在0~最大出度值--保证路径有效
np.floor(np.random.rand(outdegree.shape[0]) * outdegree)——实现向下取整&#xff0c;这样就得到了相应游走路径中接下来那个点的索引
具体实例&#xff1a;
np.floor(np.random.rand(20) * 3).astype(&#39;int64&#39;)
result: array([0, 1, 2, 1, 0, 0, 0, 0, 1, 1, 1, 2, 0, 2, 2, 2, 2, 1, 2, 0])
3. 既然知道了随机采样的序列集了&#xff0c;那么接下就是分配新的游走路径了
next_nodes &#61; [] # 用于后边存放—— 装配有下一个节点的新路径
# 参数说明&#xff1a;
succ_nodes&#xff1a;相邻节点id列表
sample_index&#xff1a;对应出度生成的随即索引集
walks_ids&#xff1a;游走路径中节点对应id号
# 接下来的循环指的是&#xff0c;将节点列表、随机采样序列、游走路径中节点对应id号一一对应进行填充--得到一个游走情况
for s, ind, walk_id in zip(succ_nodes, sample_index, walks_ids):
walks[walk_id].append(s[ind]) # 注意&#xff1a; 从开始已经知道walks&#61;>[[], [], []]是这种形式的&#xff0c;这样这里的append&#xff0c;就很容易理解成为相应节点添加可以继续前行的节点&#xff0c;形成一条路径
next_nodes.append(s[ind]) # 同时获取接下来要重新讨论游走时所需的新节点--即&#xff1a;如&#xff1a;从1走到了2&#xff0c;从3走到了7: [[1], [3]]&#61;>[[1, 2], [3, 7]]
# 接下来自然就该考虑把新的2, 7 作为下一次游走时讨论出度的节点啦
&#39;&#39;&#39;
succ_nodes &#61; self.successor(cur_nodes) # 返回可继续的节点集合
# next_nodes &#61; ...
sample_index &#61; np.floor(np.random.rand(outdegree.shape[0]) * outdegree).astype(&#39;int64&#39;)
next_nodes &#61; []
for s, ind, walk_id in zip(succ_nodes, sample_index, walks_ids):
walks[walk_id].append(s[ind])
next_nodes.append(s[ind])
######################################
cur_nodes &#61; np.array(next_nodes) # 将节点转换为np类型&#xff0c;方便一些操作运算--同时保证前后数据类型

# 遍历完游走长度的次数&#xff0c;就可以返回得到的随机游走路径啦
return walks

# 可增加轮次提高精度--epoch
# 当前参数精度大概在95%左右
!python my_deepwalk.py --use_my_random_walk --epoch 35 # 35 用自己实现的random walk训练DeepWalk模型&#xff0c;可在 ./tmp/deepwalk/walks/ 中查看构造的节点路径

[INFO] 2022-11-11 14:28:28,099 [my_deepwalk.py: 250]: Step 1200 DeepWalk Loss: 0.198106 0.242671 s/step.
[INFO] 2022-11-11 14:28:30,539 [my_deepwalk.py: 250]: Step 1210 DeepWalk Loss: 0.187183 0.309996 s/step.
[INFO] 2022-11-11 14:28:33,171 [my_deepwalk.py: 250]: Step 1220 DeepWalk Loss: 0.189533 0.244672 s/step.
[INFO] 2022-11-11 14:28:35,537 [my_deepwalk.py: 250]: Step 1230 DeepWalk Loss: 0.202293 0.232859 s/step.
[INFO] 2022-11-11 14:28:37,920 [my_deepwalk.py: 250]: Step 1240 DeepWalk Loss: 0.189366 0.244727 s/step.
[INFO] 2022-11-11 14:28:40,450 [my_deepwalk.py: 250]: Step 1250 DeepWalk Loss: 0.188601 0.254400 s/step.
[INFO] 2022-11-11 14:28:42,875 [my_deepwalk.py: 250]: Step 1260 DeepWalk Loss: 0.191343 0.247985 s/step.
[INFO] 2022-11-11 14:28:45,286 [my_deepwalk.py: 250]: Step 1270 DeepWalk Loss: 0.186549 0.255688 s/step.
[INFO] 2022-11-11 14:28:47,653 [my_deepwalk.py: 250]: Step 1280 DeepWalk Loss: 0.188638 0.240493 s/step.


[INFO] 2022-11-11 14:29:45,898 [link_predict.py: 199]: Step 180 Train Loss: 0.398023 Train AUC: 0.960870
[INFO] 2022-11-11 14:29:46,023 [link_predict.py: 223]: Step 180 Test Loss: 0.399052 Test AUC: 0.960234
[INFO] 2022-11-11 14:29:48,816 [link_predict.py: 199]: Step 190 Train Loss: 0.396805 Train AUC: 0.960916
[INFO] 2022-11-11 14:29:48,951 [link_predict.py: 223]: Step 190 Test Loss: 0.397910 Test AUC: 0.960275
[INFO] 2022-11-11 14:29:51,783 [link_predict.py: 199]: Step 200 Train Loss: 0.396290 Train AUC: 0.960936
[INFO] 2022-11-11 14:29:51,913 [link_predict.py: 223]: Step 200 Test Loss: 0.397469 Test AUC: 0.960292

2.0 SkipGram模型训练

NOTE&#xff1a;在得到节点路径后&#xff0c;node2vec会使用SkipGram模型学习节点表示&#xff0c;给定中心节点&#xff0c;预测局部路径中还有哪些节点。模型中用了negative sampling来降低计算量。



参考 PGL/examples/node2vec/node2vec.py 中的 node2vec_model 函数


2.1 SkipGram模型实现代码参考–理解


  1. 这部分的话&#xff0c;官方代码已经给的很清晰了&#xff0c;这里主要是做一些解释补充–大都可以跟上边算法公式对应着看
  2. 这里采用组合损失–组合损失计算时&#xff0c;要注意在不必要的参数创建后&#xff0c;记得关闭梯度记录–否则会对他求梯度&#xff0c;这样不太好&#xff1a;

    如:ones_label,他只是一个中间量&#xff0c;用于存放结果的&#xff0c;不需要对他求梯度&#xff0c;因为不需要优化它
  3. 还有一点&#xff0c;静态图下&#xff0c;尽量使用layers下的运算方法&#xff0c;避免出现超出计算图的一些逻辑循环操作

    这一部分没什么好说的&#xff0c;大家理解就好–多看看源码哦&#xff01;

import paddle.fluid.layers as l
def userdef_loss(embed_src, weight_pos, weight_negs):
"""
输入&#xff1a;embed_src - 中心节点向量 list (batch_size, 1, embed_size)
weight_pos - 标签节点向量 list (batch_size, 1, embed_size)
weight_negs - 负样本节点向量 list (batch_size, neg_num, embed_size)
输出&#xff1a;loss - 正负样本的交叉熵 float
"""

##################################
# 请在这里实现SkipGram的loss计算过程

### 负采样计算部分——Multi Sigmoids
# 分别计算正样本和负样本的 logits&#xff08;概率&#xff09;
pos_logits &#61; l.matmul(
embed_src, weight_pos, transpose_y&#61;True) # [batch_size, 1, 1] -- matmul:矩阵相乘
neg_logits &#61; l.matmul(
embed_src, weight_negs, transpose_y&#61;True) # [batch_size, 1, neg_num]
# 设置正样本标签&#xff0c;并计算正样本loss
ones_label &#61; pos_logits * 0. &#43; 1.
ones_label.stop_gradient &#61; True # 关闭梯度记录
pos_loss &#61; l.sigmoid_cross_entropy_with_logits(pos_logits, ones_label) # 交叉熵计算&#61;&#61;对应公式2
# 设置负样本标签&#xff0c;并计算负样本loss
zeros_label &#61; neg_logits * 0.
zeros_label.stop_gradient &#61; True
neg_loss &#61; l.sigmoid_cross_entropy_with_logits(neg_logits, zeros_label) # 交叉熵计算&#61;&#61;对应公式3
# 总的Loss计算为正样本与负样本loss之和
loss &#61; (l.reduce_mean(pos_loss) &#43; l.reduce_mean(neg_loss)) / 2 # 得到总的loss
##################################
return loss


NOTE&#xff1a;Node2Vec会根据与上个节点的距离按不同概率采样得到当前节点的下一个节点。


参考; PGL/pgl/graph_kernel.pyx 中用Cython语言实现了节点采样函数node2vec_sample

3.1 Node2Vec采样算法转换代码注解


  1. 这部分代码&#xff0c;用于随机游走后得到的路径&#xff0c;然后对这些路径进行吸收学习&#xff0c;训练图结构

import numpy as np
# 随机节点的获取
def node2vec_sample(succ, prev_succ, prev_node, p, q):
"""
输入&#xff1a;succ - 当前节点的下一个相邻节点id列表 list (num_neighbors,)
prev_succ - 前一个节点的下一个相邻节点id列表 list (num_neighbors,)
prev_node - 前一个节点id int
p - 控制回到上一节点的概率 float
q - 控制偏向DFS还是BFS float
输出&#xff1a;下一个节点id int
"""
##################################
# 请在此实现node2vec的节点采样函数
# 节点参数信息
succ_len &#61; len(succ) # 获取相邻节点id列表节点长度&#xff08;相对当前&#xff09;
prev_succ_len &#61; len(prev_succ) # 获取相邻节点id列表节点长度&#xff08;相对前一个节点&#xff09;
prev_succ_set &#61; np.asarray([]) # 前一节点的相邻节点id列表
for i in range(prev_succ_len): # 遍历得到前一节点的相邻节点id列表的新list——prev_succ_set&#xff0c;用于后边概率的讨论
# 将前一节点list&#xff0c;依次押入新的list中
prev_succ_set &#61; np.append(prev_succ_set,prev_succ[i]) # ? prev_succ_set.insert(prev_succ[i])

# 概率参数信息
probs &#61; [] # 保存每一个待前往的概率
prob &#61; 0 # 记录当前讨论的节点概率
prob_sum &#61; 0. # 所有待前往的节点的概率之和
# 遍历当前节点的相邻节点
for i in range(succ_len): # 遍历每一个当前节点前往的概率
if succ[i] &#61;&#61; prev_node: # case 1 &#xff1a; 采样节点与前一节点一致&#xff0c;那么概率为--1/q&#xff08;原地&#xff09;
prob &#61; 1. / p
# case 2 完整的应该是&#xff1a; np.where(prev_succ_set&#61;&#61;succ[i]) and np.where(succ&#61;&#61;succ[i])
# 但是因为succ本身就是采样集&#xff0c;所以np.where(succ&#61;&#61;succ[i])总成立&#xff0c;故而忽略&#xff0c;不考虑
elif np.where(prev_succ_set&#61;&#61;succ[i]): # case 2 &#xff1a; 采样节点在前一节点list内&#xff0c;那么概率为--1 ?cpython中的代码&#xff1a; prev_succ_set.find(succ[i]) !&#61; prev_succ_set.end()
prob &#61; 1.
elif np.where(prev_succ_set!&#61;succ[i]): # case 3 &#xff1a; 采样节点不在前一节点list内&#xff0c;那么概率为--1/q
prob &#61; 1. / q
else:
prob &#61; 0. # case 4 &#xff1a; other
probs.append(prob) # 将待前往的每一个节点的概率押入保存
prob_sum &#43;&#61; prob # 计算所有节点的概率之和

RAND_MAX &#61; 65535 # 这是一个随机数的最值&#xff0c;用于计算随机值的--根据C/C&#43;&#43;标准&#xff0c;最小在30000&#43;&#xff0c;这里取2^16次方
rand_num &#61; float(np.random.randint(0, RAND_MAX&#43;1)) / RAND_MAX * prob_sum # 计算一个随机概率:0~prob_sum. ?cpython中的代码: float(rand())/RAND_MAX * prob_sum
sampled_succ &#61; 0. # 当前节点的相邻节点中确定的采样点
# rand_num &#61;> 是0~prob_num的一个值&#xff0c;表示我们的截取概率阈值--即当遍历了n个节点时&#xff0c;若已遍历的节点的概率之和已经超过了rand_num
# 我们取刚好满足已遍历的节点的概率之和已经超过了rand_num的最近一个节点作为我们的采样节点
# 比如: 遍历到第5个节点时&#xff0c;权重概率和大于等于rand_num,此时第5个节点就是对应的采样的节点了
# 为了方便实现&#xff1a;这里利用循环递减--判断条件就变成了————当rand_num减到<&#61;0时&#xff0c;开始采样节点
for i in range(succ_len): # 遍历当前节点的所有相邻节点
rand_num -&#61; probs[i] # 利用rand_num这个随机获得的概率值作为依据&#xff0c;进行一个循环概率检验
if rand_num <&#61; 0: # 当遇到第一次使得rand_num减到<&#61;0后&#xff0c;说明到这个节点为止, 遍历应该终止了&#xff0c;此时的节点即未所求的节点&#xff0c;【停止检验条件】
sampled_succ &#61; succ[i] # 并把当前节点作为确定的节点
return sampled_succ # 返回待采样的节点--节点一定在succ中

[INFO] 2022-11-11 14:38:49,133 [link_predict.py: 199]: Step 170 Train Loss: 0.454199 Train AUC: 0.954936
[INFO] 2022-11-11 14:38:49,260 [link_predict.py: 223]: Step 170 Test Loss: 0.454974 Test AUC: 0.954118
[INFO] 2022-11-11 14:38:51,997 [link_predict.py: 199]: Step 180 Train Loss: 0.452219 Train AUC: 0.955133
[INFO] 2022-11-11 14:38:52,122 [link_predict.py: 223]: Step 180 Test Loss: 0.453069 Test AUC: 0.954312
[INFO] 2022-11-11 14:38:54,851 [link_predict.py: 199]: Step 190 Train Loss: 0.450969 Train AUC: 0.955254
[INFO] 2022-11-11 14:38:54,978 [link_predict.py: 223]: Step 190 Test Loss: 0.451892 Test AUC: 0.954428
[INFO] 2022-11-11 14:38:57,714 [link_predict.py: 199]: Step 200 Train Loss: 0.450440 Train AUC: 0.955305
[INFO] 2022-11-11 14:38:57,842 [link_predict.py: 223]: Step 200 Test Loss: 0.451436 Test AUC: 0.954473


1. 回顾并总结了图的基本概念。

2. 学习思考算法实现的代码思路--Node2Vec的实现以及RandomWalk的实现。

3. 对源码阅读能力的提升。

其它相关笔记:

关于图计算&图学习的基础知识概览&#xff1a;前置知识点学习&#xff08;PGL&#xff09;[系列一] https://aistudio.baidu.com/aistudio/projectdetail/4982973?contributionType&#61;1
图机器学习(GML)&图神经网络(GNN)原理和代码实现(前置学习系列二&#xff09;&#xff1a;https://aistudio.baidu.com/aistudio/projectdetail/4990947?contributionType&#61;1


* 如果我的项目对你有帮助不如点一个心&#xff0c;fork一下&#xff0c;以备可以常复习哦&#xff01;

推荐阅读
  • 开发心得:利用 Redis 构建分布式系统的轻量级协调机制
    开发心得:利用 Redis 构建分布式系统的轻量级协调机制 ... [详细]
  • Python与R语言在功能和应用场景上各有优势。尽管R语言在统计分析和数据可视化方面具有更强的专业性,但Python作为一种通用编程语言,适用于更广泛的领域,包括Web开发、自动化脚本和机器学习等。对于初学者而言,Python的学习曲线更为平缓,上手更加容易。此外,Python拥有庞大的社区支持和丰富的第三方库,使其在实际应用中更具灵活性和扩展性。 ... [详细]
  • PHP中元素的计量单位是什么? ... [详细]
  • 理工科男女不容错过的神奇资源网站
    十一长假即将结束,你的假期学习计划进展如何?无论你是在家中、思念家乡,还是身处异国他乡,理工科学生都不容错过一些神奇的资源网站。这些网站提供了丰富的学术资料、实验数据和技术文档,能够帮助你在假期中高效学习和提升专业技能。 ... [详细]
  • Ceph API微服务实现RBD块设备的高效创建与安全删除
    本文旨在实现Ceph块存储中RBD块设备的高效创建与安全删除功能。开发环境为CentOS 7,使用 IntelliJ IDEA 进行开发。首先介绍了 librbd 的基本概念及其在 Ceph 中的作用,随后详细描述了项目 Gradle 配置的优化过程,确保了开发环境的稳定性和兼容性。通过这一系列步骤,我们成功实现了 RBD 块设备的快速创建与安全删除,提升了系统的整体性能和可靠性。 ... [详细]
  • 在 Linux 系统中,`/proc` 目录实现了一种特殊的文件系统,称为 proc 文件系统。与传统的文件系统不同,proc 文件系统主要用于提供内核和进程信息的动态视图,通过文件和目录的形式呈现。这些信息包括系统状态、进程细节以及各种内核参数,为系统管理员和开发者提供了强大的诊断和调试工具。此外,proc 文件系统还支持实时读取和修改某些内核参数,增强了系统的灵活性和可配置性。 ... [详细]
  • voc生成xml 代码
    目录 lxmlwindows安装 读取示例 可视化 生成示例 上面是代码,下面有调用示例 api调用代码,其实只有几行:这个生成代码也很简 ... [详细]
  • 表面缺陷检测数据集综述及GitHub开源项目推荐
    本文综述了表面缺陷检测领域的数据集,并推荐了多个GitHub上的开源项目。通过对现有文献和数据集的系统整理,为研究人员提供了全面的资源参考,有助于推动该领域的发展和技术进步。 ... [详细]
  • PyQt5 QTextEdit:深入解析Python中多功能GUI库的应用与实现
    本文详细探讨了 PyQt5 中 QTextEdit 组件在 Python 多功能 GUI 库中的应用与实现。PyQt5 是 Qt 框架的 Python 绑定,提供了超过 620 个类和 6000 个函数及方法,广泛应用于跨平台应用程序开发。QTextEdit 作为其中的重要组件,支持丰富的文本编辑功能,如富文本格式、文本高亮和自定义样式等。PyQt5 的流行性不仅在于其强大的功能,还在于其易用性和灵活性,使其成为开发复杂用户界面的理想选择。 ... [详细]
  • 本文探讨了将PEBuilder转换为DIBooter.sh的方法,重点介绍了如何将DI工具集成到启动层,实现离线镜像引导安装。通过使用DD命令替代传统的grub-install工具,实现了GRUB的离线安装。此外,还详细解析了bootice工具的工作原理及其在该过程中的应用,确保系统在无网络环境下也能顺利引导和安装。 ... [详细]
  • 本文深入探讨了 C# 中 `SqlCommand` 和 `SqlDataAdapter` 的核心差异及其应用场景。`SqlCommand` 主要用于执行单一的 SQL 命令,并通过 `DataReader` 获取结果,具有较高的执行效率,但灵活性较低。相比之下,`SqlDataAdapter` 则适用于复杂的数据操作,通过 `DataSet` 提供了更多的数据处理功能,如数据填充、更新和批量操作,更适合需要频繁数据交互的场景。 ... [详细]
  • Python 并发编程进阶:从初学者到高手的进程与模块开发指南
    Python 并发编程进阶:从初学者到高手的进程与模块开发指南 ... [详细]
  • 使用React与Ant Design 3.x构建IP地址输入组件
    本文深入探讨了利用React框架结合Ant Design 3.x版本开发IP地址输入组件的方法。通过详细的代码示例,展示了如何高效地创建具备良好用户体验的IP输入框,对于前端开发者而言具有较高的实践指导意义。 ... [详细]
  • HTML5 Web存储技术是许多开发者青睐本地应用程序的重要原因之一,因为它能够实现在客户端本地存储数据。HTML5通过引入Web Storage API,使得Web应用程序能够在浏览器中高效地存储数据,从而提升了应用的性能和用户体验。相较于传统的Cookie机制,Web Storage不仅提供了更大的存储容量,还简化了数据管理和访问的方式。本文将从基础概念、关键技术到实际应用,全面解析HTML5 Web存储技术,帮助读者深入了解其工作原理和应用场景。 ... [详细]
  • 开发笔记:校园商铺系统中店铺注册功能模块的Controller层优化与重构
    开发笔记:校园商铺系统中店铺注册功能模块的Controller层优化与重构 ... [详细]
author-avatar
魏蚊瑞
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有